<?php


namespace App\Remote\Feishu\Message;

use App\Exceptions\BaseException;
use App\Kuafu\Base\Enum\BaseCodeEnum;
use App\Remote\Remote;
use Closure;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Request as RequestAlias;
use Symfony\Component\HttpFoundation\Response as ResponseAlias;


class FeishuRemote extends Remote
{

    protected static array $instance = [];

    protected string $appId;
    protected string $appSecret;


    public function __construct()
    {
        $this->appId     = config('feishu.center.appID');
        $this->appSecret = config('feishu.center.AppSecret');
    }


    /**
     * @return static
     */
    public static function getInstance(): self
    {
        if (!isset(self::$instance[static::class])) {
            self::$instance[static::class] = new static();
        }
        return self::$instance[static::class];
    }


    public static function getBaseUri(): string
    {
        // TODO: Implement getBaseUri() method.
        return "https://open.feishu.cn";
    }


    public static function registerResponseHandle(): Closure
    {
        return function (string $data) {
            return json_decode($data, true);
        };
        // TODO: Change the autogenerated stub
    }

    /**
     * @param string $uri
     * @param string $requestMethod
     * @param array $params
     * @param array $headers
     * @param bool $throwException
     * @return mixed|void
     * @throws GuzzleException
     */
    public final static function customRequest(
        string $uri,
        string $requestMethod,
        array $params = [],
        array $headers = [],
        bool $throwException = false
    ) {
        try {
            $client = new Client(['verify' => false, "handler" => static::getHandlerStack()]);

            $requestUri = static::getBaseUri() . $uri;
            $headers    = array_merge(static::buildBasicHeaders(), $headers);

            $body    = null;
            $options = [
                "http_errors" => self::$httpError,
                "timeout"     => static::$requestTimeOut
            ];

            if (strtoupper($requestMethod) == RequestAlias::METHOD_POST && !empty($params)) {
                $body            = json_encode($params);
                $lowerKeysHeader = array_change_key_case($headers, CASE_LOWER);
                if (isset($lowerKeysHeader["content-type"])) {
                    switch (strtolower($lowerKeysHeader["content-type"])) {
                        case "application/json":
                            $body = json_encode($params);
                            break;
                        case "application/x-www-form-urlencoded":
                            $body = http_build_query($params);
                            break;
                        case "multipart/form-data":
                            $options = array_merge($options, $params);
                            $headers = $params = [];
                            break;
//                        default:
//                            throw new Exception("未设置远程请求 content-type");
                    }
                }
            }

            $request = new Request(
                $requestMethod,
                strtoupper($requestMethod) == RequestAlias::METHOD_GET && $params ? $requestUri . "?" . http_build_query($params) : $requestUri,
                $headers,
                $body
            );

            $response         = $client->send($request, $options);
            $responseHttpCode = $response->getStatusCode();
            $responseContent  = $response->getBody()->getContents();


            if ($responseHttpCode == ResponseAlias::HTTP_OK) {
                return (static::registerResponseHandle())($responseContent);
            } else {
                throw new Exception($responseContent, BaseCodeEnum::ERR_REMOTE_REQUEST);
            }

        } catch (Exception $exception) {
            if ($throwException) {
                throw $exception;
            } else {
                handleException($exception, BaseCodeEnum::ERR_REMOTE_REQUEST);
            }
        }
    }

    /**
     * @param string $uri
     * @param string $requestMethod
     * @param array|string|null $params
     * @param array|null $headers
     * @param Closure|null $handleResponseClosure
     * @param int $cacheTime
     * @param bool $throwException
     * @return mixed
     * @throws GuzzleException|BaseException
     */
    public static function customRequestV3(
        string $uri,
        string $requestMethod,
        $params = null,
        array $headers = null,
        Closure $handleResponseClosure = null,
        int $cacheTime = 0,
        bool $throwException = false
    ) {
        try {
            $requestFunc = function () use ($uri, $requestMethod, $params, $headers, $handleResponseClosure) {

                $client = new Client(['verify' => false, "handler" => static::getHandlerStack()]);
                //请求地址
                $requestUri = Str::startsWith($uri, ["http", "https"]) ? $uri : vsprintf(
                    "%s/%s",
                    [trim(static::getBaseUri(), "/"), trim($uri, "/")]
                );

                //请求头信息
                $requestHeaders = array_filter(array_merge(
                    array_change_key_case(static::buildBasicHeaders(), CASE_LOWER),
                    array_change_key_case($headers ?? [], CASE_LOWER)
                ));

                $body    = null;
                $options = [
                    "http_errors" => static::$httpError,
                    "timeout"     => static::$requestTimeOut
                ];

                if (isset($requestHeaders["content-type"]) && !empty($params)) {
                    switch (strtolower($requestHeaders["content-type"])) {
                        case "application/json":
                            $body = json_encode($params);
                            break;
                        case "application/x-www-form-urlencoded":
                            $body = http_build_query($params);
                            break;
                        case "multipart/form-data":
                            $options = array_merge($options, $params);
                            unset($requestHeaders["content-type"]);
                            $body = null;
                            break;
                        case "application/xml":
                            $body = $params;
                            break;
                    }
                }

                $request = new Request(
                    $requestMethod,
                    $requestUri,
                    $requestHeaders,
                    $body
                );

                $response                 = $client->send($request, $options);
                $responseOriginalContents = $response->getBody()->getContents();
                $responseHttpCode         = $response->getStatusCode();

                if ($handleResponseClosure instanceof Closure) {
                    return $handleResponseClosure($responseHttpCode, $responseOriginalContents);
                }
                return (static::registerResponseHandle())($responseHttpCode, $responseOriginalContents);
            };

            if ($cacheTime > 0) {
                $args     = func_get_args();
                $args[3]  = array_merge(static::buildBasicHeaders(), $args[3] ?? []);
                $cacheKey = $cacheKey ?? sprintf(
                        "KF:OS:REMOTE:%s",
                        strtoupper(md5(json_encode($args)))
                    );

                return Cache::remember(
                    $cacheKey,
                    $cacheTime,
                    $requestFunc
                );
            } else {
                return $requestFunc();
            }
        } catch (Exception $exception) {
            if (!$throwException) {
                handleException(
                    $exception,
                    " 远程调用失败 [ " . class_basename(static::class) . " ] [ {$exception->getMessage()} ]",
                    BaseCodeEnum::ERR_REMOTE_REQUEST,
                    null,
                    static::registerFsRobot()
                );
            }
            throw $exception;
        }
    }
}
